//SNTP client
#define __SNTPCLIENT_C



#include "TCPIP Stack/TCPIP.h"


#if defined(STACK_USE_SNTP_CLIENT)

#define NTP_LENGTH			(48ul)
//Time server port.  NIST servers use 123
#define TIME_SERVER_PORT 	(123ul)

//Time server from list at 
// http://tf.nist.gov/tf-cgi/servers.cgi

//"nist.netservicesgroup.com"; 
//64.113.32.5
//NODE_INFO sntpServerInfo = {  0x11111111 /*MAC*/, 0x05207140 /*IP*/};
//!!!192.168.0.100
NODE_INFO sntpServerInfo = {  {'a','b','c','d','e',0} /*MAC*/, 0x6400A8C0 /*IP*/};


DWORD_VAL lastNTP_Seconds = 0x00000000;

BOOL fetchTime = FALSE;
BOOL newTime = FALSE;


void SNTP_Client(void)
{
	WORD				i, len;
	BOOL				result;	
	static UDP_SOCKET 	udpSocket = INVALID_SOCKET;
	static TICK			timer;
	static enum _sntpState 	
		{
			STATE_HOME = 0,
			STATE_RESOLVE_NAME,
			STATE_ARP_START_RESOLVE,
			STATE_ARP_RESOLVE,
			STATE_OPEN_SOCKET,
			STATE_REQUEST,
			STATE_RESPONSE,
			STATE_CLOSE_SOCKET,
			STATE_DONE
		}sntpState = STATE_DONE;


	switch(sntpState)
	{/*
		case STATE_HOME:
			// Obtain ownership of the DNS resolution module
			if(!DNSBeginUsage())
				break;

			// Obtain the IP address associated with the common ServerName
			DNSResolve(sntpServerName, DNS_TYPE_A);
			sntpState++;
			break;

		case STATE_RESOLVE_NAME:
			// Wait for the DNS server to return the requested IP address
			if(!DNSIsResolved(&timeServer.IPAddr))
				break;

			// Release the DNS module, we no longer need it
			if(!DNSEndUsage())
			{
				// An invalid IP address was returned from the DNS 
				// server.  Quit and fail permanantly if host is not valid.
				sntpState = STATE_DONE;
				break;
			}

			sntpState++;*/
	    case STATE_HOME:
	    	sntpState = STATE_ARP_START_RESOLVE;
		case STATE_ARP_START_RESOLVE:
			// Obtain the MAC address associated with the server's IP address (either direct MAC address on same subnet, or the MAC address of the Gateway machine)
			ARPResolve(&sntpServerInfo.IPAddr);
			timer = TickGet();
			sntpState++;
			break;

		case STATE_ARP_RESOLVE:
			// Wait for the MAC address to finish being obtained
			if(!ARPIsResolved(&sntpServerInfo.IPAddr, &sntpServerInfo.MACAddr))
			{
				// Time out if too much time is spent in this state
				if(TickGet()-timer > 1*TICK_SECOND)
				{
					// Retransmit ARP request
					sntpState--;
				}
				break;
			}
			sntpState++;

		case STATE_OPEN_SOCKET:
			udpSocket = UDPOpen(TIME_SERVER_PORT, &sntpServerInfo, TIME_SERVER_PORT);
			if(udpSocket == INVALID_SOCKET)
				break;
	
			timer = TickGet();
			sntpState++;
			break;

		case STATE_REQUEST:
			if(!UDPIsPutReady(udpSocket))
			{
				// Time out if too much time is spent in this state
				if(TickGet()-timer > 5*TICK_SECOND)
				{
					// Close the socket so it can be used by other modules
					UDPClose(udpSocket);
					udpSocket = INVALID_SOCKET;
					sntpState--;
				}
				break;
			}

			//Send the time request
			result = UDPPut(0x23); //version 4, and client mode
			for(i = 1; i<NTP_LENGTH && result == TRUE; i++)
			{
				result = UDPPut(0x00);
			}
			
			//Send packet
			UDPFlush();
			
			//If transfer succeeded, move to next state, otherwise retry
			if(result)
			{	
				sntpState++;
			}
			else
			{
				break;
			}

		case STATE_RESPONSE:
			if(!UDPIsGetReady(udpSocket))
				break;

			//Receive NTP packet
			// We only care about bytes 44-48 which is the seconds field of
			//  the time stamp.  We can drop all other data
			for(i = 0; i< (12ul); i++)
			{
				len = UDPGetArray((BYTE*)&lastNTP_Seconds, sizeof(DWORD_VAL));
				if(i != NTP_LENGTH)
				{
					break;
				}
			}

									
			//Mark that we have a new time
			newTime = TRUE;
			//Transfer Successful, close socket
				
			sntpState++;

		case STATE_CLOSE_SOCKET:
			UDPDiscard();
			sntpState++;
			break;
			 
		case STATE_DONE:
			//Wait until getNTPTime() is called
			if(fetchTime == TRUE)
			{
				sntpState = STATE_HOME;
				fetchTime = FALSE;
			}
			break;

	}

} /* end of SNTP_Client */

//Called by the outside world to initiate an NTP request
// returns true if new time has arrived
// returns false otherwise
// new time is passed out through _time_
BOOL getNTPTime(DWORD_VAL* time )
{
	static BYTE waitingForTime = FALSE;
	
	if(!waitingForTime)
	{
		//Cause the task state machine to exit STATE_DONE
		//  and return to STATE_HOME
		fetchTime = TRUE;
		waitingForTime = TRUE;
	}
	else
	{
		if(newTime)
		{
			time->word.LW = lastNTP_Seconds.word.LW;
			time->word.HW = lastNTP_Seconds.word.HW;
			waitingForTime = FALSE;
			newTime = FALSE;
			return TRUE;		
		}
	}
	return FALSE;
} /* end of getNTPTime */	
#endif /*defined(STACK_USE_SNTP_CLIENT)*/
